Chapter 11
COM and MFC

by Gene Olafsen

In This Chapter

  Understanding the Afx Global Functions 410
  Reviewing the OLE Macros 420
  MFC and the OLE Class Categories 434

MFC provides a powerful set of classes, macros, and global functions to provide access to a sizeable portion of the vast OLE empire.

Understanding the Afx Global Functions

The MFC framework prefixes global public functions with the letters Afx. These functions are available to you whether you develop an EXE-based (CWinApp-derived) application or a DLL-based (COleControlModule-derived) object server. There are little more than two dozen Afx functions, and they basically all either perform initialization operations or offer some kind of object lifetime support.

Application Lifetime Control

A number of functions are globally available to an MFC-based application or DLL that enable you to control the circumstances under which the program will terminate or the DLL will unload.

AfxOleCanExitApp

The AfxOleCanExitApp function returns a Boolean value indicating whether or not the application can terminate. The AfxOleLockApp and AfxOleUnlockApp functions increment and decrement, respectively, the m_nObjectCount variable. This function simply returns a value based on the state of that counter.

BOOL AFXAPI AfxOleCanExitApp()
{
   AFX_MODULE_STATE* pModuleState = AfxGetModuleState();
   return pModuleState->m_nObjectCount == 0;
}

In addition, this method is called internally by the MFC framework to determine whether an application can terminate or an OLE-server DLL can unload.

SCODE AFXAPI AfxDllCanUnloadNow(void)
{
   // return S_OK only if no outstanding objects active
   if (!AfxOleCanExitApp())
       return S_FALSE;

   // check if any class factories with >1 reference count
   AFX_MODULE_STATE* pModuleState = AfxGetModuleState();
}

AfxOleGetMessageFilter

The AFxOleGetMessageFilter retrieves the application object’s current message filter. The message filter object that this function returns derives from the COleMessageFilter class.

_AFXWIN_INLINE COleMessageFilter* AFXAPI AfxOleGetMessageFilter()
{
ASSERT_VALID(AfxGetThread());
return AfxGetThread()->m_pMessageFilter;
}

AfxOleGetUserCtrl

The AfxOleGetUserCtrl function retrieves the current state of the user-control flag. When the application was launched by the OLE system DLLs, the user is not considered “in control”; thus this function returns a FALSE value. Such a condition exists when the application is launched with command-line arguments that indicate the conditions under which the application was started. The companion set function (AfxOleSetUserCtrl) is seen setting the variable retrieved by this function to FALSE under those conditions where the application is launched with such arguments.

void CCommandLineInfo::ParseParamFlag(const char* pszParam)
{
   // OLE command switches are case insensitive, while
   // shell command switches are case sensitive

   if (lstrcmpA(pszParam, “pt”) == 0)
       m_nShellCommand = FilePrintTo;
   else if (lstrcmpA(pszParam, “p”) == 0)
       m_nShellCommand = FilePrint;
   else if (lstrcmpiA(pszParam, “Unregister”) == 0 ||
            lstrcmpiA(pszParam, “Unregserver”) == 0)
       m_nShellCommand = AppUnregister;
   else if (lstrcmpA(pszParam, “dde”) == 0)
   {
       AfxOleSetUserCtrl(FALSE);
       m_nShellCommand = FileDDE;
   }
   else if (lstrcmpiA(pszParam, “Embedding”) == 0)
   {
       AfxOleSetUserCtrl(FALSE);
       m_bRunEmbedded = TRUE;
       m_bShowSplash = FALSE;
   }
   else if (lstrcmpiA(pszParam, “Automation”) == 0)
   {
       AfxOleSetUserCtrl(FALSE);
       m_bRunAutomated = TRUE;
       m_bShowSplash = FALSE;
   }
}

Otherwise, the code for the AfxOleGetUserCtrl function is very straightforward, returning the value of a global variable.

BOOL AFXAPI AfxOleGetUserCtrl()
{
   AFX_MODULE_STATE* pModuleState = AfxGetModuleState();
   return pModuleState->m_bUserCtrl;
}

AfxOleSetUserCtrl

The AfxOleSetUserCtrl function accepts a Boolean argument, and you can use it to set or clear the user-control flag. The conditions under which this flag is called are described in more detail in the previous section.

It is interesting to note, however, the framework code that implements this function. In the debug build of the libraries, the application does not shut down if you have obtained control of the application.

void AFXAPI AfxOleSetUserCtrl(BOOL bUserCtrl)
{
   AFX_MODULE_STATE* pModuleState = AfxGetModuleState();
#ifdef _DEBUG
   CWinApp* pApp = AfxGetApp();
   if (bUserCtrl && !pModuleState->m_bUserCtrl &&
       (pApp == NULL || pApp->m_pMainWnd == NULL ||
       !pApp->m_pMainWnd->IsWindowVisible()))
   {
// If the user gets control while the application window is
//  not visible, the application may not shut down when the object
       //  count reaches zero.
       TRACE0(“Warning: AfxOleSetUserCtrl(TRUE) called \
              with application window hidden.\n”);
   }
#endif
   pModuleState->m_bUserCtrl = bUserCtrl;
}

AfxOleLockApp

The AfxOleLockApp function increments a global lock count variable. MFC keeps track of the number of OLE objects that are active and calls this function accordingly. The framework uses the InterlockedIncrement function to adjust the value of the pModuleState->m_nObjectCount variable. This function synchronizes access to a variable and prevents more than one thread from accessing that variable simultaneously.

void AFXAPI AfxOleLockApp()
{
   AFX_MODULE_STATE* pModuleState = AfxGetModuleState();
   InterlockedIncrement(&pModuleState->m_nObjectCount);
}

AfxOleUnlockApp

The AfxOleUnlockApp function is the counter-function to AfxOleLockApp. This function decrements the framework’s object counter variable in a thread-safe manner.

void AFXAPI AfxOleUnlockApp()
{
   AFX_MODULE_STATE* pModuleState = AfxGetModuleState();
   ASSERT(pModuleState->m_nObjectCount != 0);
   if (InterlockedDecrement(&pModuleState->m_nObjectCount) == 0)
   {
       // allow application to shut down when all the objects have
       //  been released
       ::AfxOleOnReleaseAllObjects();
   }
}

AfxOleRegisterServerClass

The AfxOleRegisterServerClass function provides a mechanism for you to register your server in the system Registry. The function takes a large number of parameters, but the gist of this function’s purpose is to provide more control over the registration process than the Register function in COleTemplateServer offers.



The type of OLE application that this function registers is specified by the OLE_APPTYPE enumeration. Valid application types include the following:

  Full in-place document server (OAT_INPLACE_SERVER)
  Server that supports only embedding (OAT_SERVER)
  Container that supports links to embeddings (OAT_CONTAINER)
  Automation-capable object (OAT_DISPATCH_OBJECT)

AfxOleSetEditMenu

The AfxOleSetEditMenu function offers additional flexibility in determining the manner in which OLE-server verbs are displayed in menus, above and beyond the way the default COleDocument implements this feature.

COleDocument uses the command ID range, ID_OLE_VERB_FIRST through ID_OLE_VERB_LAST, for the verbs that the selected OLE server supports. At runtime, the text description of the verb updates the appropriate menu. The AfxOleSetEditMenu function allows many of the default values for these operations to be changed.

Client Control Management

There are three global functions supporting control containment and creation: AfxEnableControlContainer, AfxOleLockControl, and AfxOleUnlockControl.

AfxEnableControlContainer

The AfxEnableControlContainer function enables support for OLE-control containment by an application. This function is automatically inserted into your application’s InitInstance function if you specified ActiveX control support when creating your application with AppWizard.

AfxOleLockControl

The AfxOleLockControl function locks the class factory for the specified control in memory. Locking a control’s class factory significantly speeds up the creation of controls. This function has the additional advantage by keeping controls of this type in memory between display occurrences of a dialog box containing such controls.

This function is overloaded and has two different argument lists. The first takes the CLSID (class ID) of the control, and the other accepts the control’s ProgID (program ID).

AfxOleUnlockControl

The AfxOleUnlockControl function unlocks the class factory for the specified control. As with the lock function, this unlock function is overloaded to accept either the class factory’s CLSID or ProgID. Turning the class factory locking feature off adversely affects ActiveX control creation and dialog box display response.

Connection Point Management

Connection points offer an implementation-independent mechanism for managing outgoing COM interfaces.

AfxConnectionAdvise

The AfxConnectionAdvise function establishes a connection between a COM server connection source (the caller) and a connection sink (the callee). The concept of connection points is a very powerful one. It is, however, somewhat confusing, so I will take a bit more time to explain its use. AfxConnectionAdvise accepts five parameters:

  A pointer to the object calling the interface (an OLE server)
  The interface ID of the object that calls the interface
  A pointer to the object that implements the interface (an OLE container)
  A flag that indicates whether or not creating the connection increments the reference count on the object that implements the interface
  A pointer to a connection identifier (a cookie)

You commonly use this function to configure CCmdTarget-derived classes as sinks for OLE-server object events. By examining the MFC framework code, you will be able to understand the purpose of the function’s arguments and the mechanics that involve setting up a connection.

First, a pointer to the connection point container is obtained. Then an interface pointer to the connection point for the specified IID is obtained. Finally, the advise method is called, establishing a connection between source and sink.

BOOL AFXAPI AfxConnectionAdvise(LPUNKNOWN pUnkSrc, REFIID iid,
   LPUNKNOWN pUnkSink, BOOL bRefCount, DWORD* pdwCookie)
{
   ASSERT_POINTER(pUnkSrc, IUnknown);
   ASSERT_POINTER(pUnkSink, IUnknown);
   ASSERT_POINTER(pdwCookie, DWORD);
   BOOL bSuccess = FALSE;
   LPCONNECTIONPOINTCONTAINER pCPC;
   if (SUCCEEDED(pUnkSrc->QueryInterface(
                   IID_IConnectionPointContainer,
                   (LPVOID*)&pCPC)))
   {
       ASSERT_POINTER(pCPC, IConnectionPointContainer);
       LPCONNECTIONPOINT pCP;
       if (SUCCEEDED(pCPC->FindConnectionPoint(iid, &pCP)))
       {
           ASSERT_POINTER(pCP, IConnectionPoint);
           if (SUCCEEDED(pCP->Advise(pUnkSink, pdwCookie)))
               bSuccess = TRUE;
           pCP->Release();
           if (bSuccess && !bRefCount)
               pUnkSink->Release();
       }
       pCPC->Release();
   }
   return bSuccess;
}

Only three steps are required to use the AfxConnectionAdvise function:

1.  Derive a class from CCmdTarget, CMyClass, and create a dispatch map entry that matches the signature of the event you want to receive. In this case, the event’s name is MyEvent and it has no return value. It does, however, require a BSTR argument. You must implement the OnMyEvent function as a member function of CMyClass.
BEGIN_DISPATCH_MAP(CMyClass, CCmdTarget)
DISP_FUNCTION_ID(CMyClass,“MyEvent”,1, \
OnMyEvent,VT_EMPTY,VTS_BSTR)
END_DISPATCH_MAP()
2.  Create an instance of the sink class:
pSink = new CMyClass();
3.  Before you can hook up the sink interface with the server so that you can start to receive events, you must obtain a pointer to the dispinterface of CMyClass. By deriving your class from CCmdTarget, you have an object that supports the IDispatch interface.
LPUNKNOWN pUnkSink = pSink->GetIDispatch(FALSE);
4.  Now you can establish a connection between the source and your sink—MyClass. The function returns a cookie to identify the connection.
AfxConnectionAdvise(m_pUnkSrc, IID_MYEVENT,
pUnkSink, FALSE, &m_dwCookie);

AfxConnectionUnadvise

The AfxConnectionUnadvise function disconnects a connection previously established with AfxConnectionAdvise. This function accepts the same arguments as its advise counterpart.

BOOL AFXAPI AfxConnectionUnadvise(LPUNKNOWN pUnkSrc, REFIID iid,
    LPUNKNOWN pUnkSink, BOOL bRefCount, DWORD dwCookie)
{
    ASSERT_POINTER(pUnkSrc, IUnknown);
    ASSERT_POINTER(pUnkSink, IUnknown);
    if (!bRefCount)
        pUnkSink->AddRef();
    BOOL bSuccess = FALSE;
    LPCONNECTIONPOINTCONTAINER pCPC;
    if (SUCCEEDED(pUnkSrc->QueryInterface(
                    IID_IConnectionPointContainer,
                    (LPVOID*)&pCPC)))
    {
        ASSERT_POINTER(pCPC, IConnectionPointContainer);
        LPCONNECTIONPOINT pCP;
        if (SUCCEEDED(pCPC->FindConnectionPoint(iid, &pCP)))
        {
            ASSERT_POINTER(pCP, IConnectionPoint);
            if (SUCCEEDED(pCP->Unadvise(dwCookie)))
                bSuccess = TRUE;
            pCP->Release();
        }
        pCPC->Release();
    }
    // If we failed, undo the earlier AddRef.
    if (!bRefCount && !bSuccess)
        pUnkSink->Release();
    return bSuccess;
}

Control Registration

One of the two requirements a component must fulfill to be considered an ActiveX control is control registration. (The other requirement is that the component must support the IUnknown interface.) The methods in this section identify control registration and unregistration.

AfxOleRegisterControlClass

The AfxOleRegisterControlClass function registers a control class with the Windows operating system. This function accepts ten parameters, updating the Registry with a number of control attributes including the threading model the control supports.

AfxOleRegisterPropertyPageClass

The AfxOleRegisterPropertyPageClass function registers a property page class with the Windows operating system. It is necessary to register all the custom property pages you create for your control or other COM object.



AfxOleRegisterTypeLib

The AfxOleRegisterTypeLib function updates the Windows Registry with information about the location and identity of a type library. This function requires the HINSTANCE of the application, the file path to the type library (.TLB) file, and the GUID of the type library.

AfxOleUnregisterClass

The AfxOleUnregisterClass function removes a property page or ActiveX control’s registration from the Windows Registry. This function requires both the object’s GUID and ProgID.

AfxOleUnregisterTypeLib

The AfxOleRegisterClass function requires only the type library’s GUID and removes its entry from the Windows Registry.

Exceptions

You are probably already familiar with the MFC exception-handling functions and classes. Two additional functions have been added to throw exceptions that relate to OLE activity: AfxThrowOleDispatchException and AfxThrowOleException.

AfxThrowOleDispatchException

Use the AfxThrowOleDispatchException function to indicate a problem with an automation server. This function is overloaded to either accept descriptive text for the user as a string of characters in your code, or to accept a resource ID, from which the system obtains the necessary text.

AfxThrowOleException

The AfxThrowOleException function is overloaded and provides a version that accepts an SCODE and another that accepts an HRESULT. Both functions create the necessary COleException object for you. Your exception handling code (you did provide exception handling in your code—right?) receives the COleException object and can take the appropriate action.

Initialization

Before you can issue any calls that involve COM, you must initialize the OLE system libraries.

AfxOleInit

The AfxOleInit function initializes the OLE DLLs. The AppWizard will automatically include this call at the beginning (yes, it is that important) of your application’s InitInstance function.

Here’s a quick review of what the initialization steps include:

  Send a call to initialize the COM library as a single-thread apartment (STA).
  Issue any nonsuccess initialization messages.
  Obtain the current thread, and then create and attach a COleMessageFilter object.
  Register the message filter with the OLE system DLLs.

The preceding steps are performed in the MFC code for OLE initialization, shown in the following:

BOOL AFXAPI AfxOleInit()
{
    _AFX_THREAD_STATE* pState = AfxGetThreadState();
    ASSERT(!pState->m_bNeedTerm);    // calling it twice?
    // during a DLL_PROCESS_DETACH.
    if (afxContextIsDLL)
    {
        pState->m_bNeedTerm = -1;  // -1 is a special flag
        return TRUE;
    }
    // first, initialize OLE
    SCODE sc = ::OleInitialize(NULL);
    if (FAILED(sc))
    {
        // warn about non-NULL success codes
        TRACE1(“Warning: OleInitialize returned scode = %s.\n”,
            AfxGetFullScodeString(sc));
        goto InitFailed;
    }
    // termination required when OleInitialize does not fail
    pState->m_bNeedTerm = TRUE;
    // hook idle time and exit time for required OLE cleanup
    CWinThread* pThread; pThread = AfxGetThread();
    pThread->m_lpfnOleTermOrFreeLib = AfxOleTermOrFreeLib;
    // allocate and initialize default message filter
    if (pThread->m_pMessageFilter == NULL)
    {
        pThread->m_pMessageFilter = new COleMessageFilter;
        ASSERT(AfxOleGetMessageFilter() != NULL);
        AfxOleGetMessageFilter()->Register();
    }
    return TRUE;
InitFailed:
    AfxOleTerm();
    return FALSE;
}

AfxOleInitModule

The AfxOleInitModule is for DLLs what AfxOleInit is for MFC applications. AfxOleInitModule initializes the OLE DLLs.

If you are using the ControlWizard to create the skeleton for your control application, you will not find a call directly to AfxOleInitModule. The reason is that your InitInstance function calls COleControlModule::InitInstance, and that function makes the appropriate calls to initialize the OLE DLLs for you.

Licensing

More recent additions to the OLE specification include COM object licensing support. With licensing, you can control the number of components a class factory creates. So if you are building a source control management system based on COM, each user’s connection to the server component might require connection by a licensed client object.

AfxVerifyLicFile

The AfxVerifyLicFile function verifies the existence of a license file for a control.

Type Information

Type information describes a COM object’s interfaces. Such information allows a program requiring services provided by the component to be identified at runtime. In addition, a design tool can use this information to display editing dialogs for properties and methods.

AfxOleTypeMatchGuid

The AfxOleTypeMatchGuid function determines if the given type descriptor describes a particular interface. You can easily obtain type information that an object provides using the ITypeInfo interface. This function allows you to work backwards in a sense: You already have type information, and you want to verify the object which it describes.

Reviewing the OLE Macros

MFC’s support for OLE requires the use of a substantial number of macros. The purpose of these macros spans the range of functionality that OLE offers. The common theme running through these macro categories is that most offer some kind of map construct. MFC programmers have long been familiar with the message map architecture prevalent through the framework, especially with regard to the document-view architecture.

Class Factories

When you think of the word factory, you might conjure up images of car parts moving down an assembly line, one after the other, while various welding and stamping machines take their turn bending and shaping these pieces. The concept of a factory isn’t much different when describing the operation of an OLE class factory. A class factory’s purpose is to create multiple objects of the same type. The rationale behind having class factories is to define a generic model for an action that you will need to use in countless situations in the COM universe. Factory classes implement either the IClassFactory or IClassFactory2 interface, where the latter interface offers object creation through a license.

DECLARE_OLECREATE

The DECLARE_OLECREATE macro enables CCmdTarget-derived classes to be created through OLE automation. This macro requires one argument—the name of the class to create. This macro must appear in the class definition.

DECLARE_OLECREATE_EX

The DECLARE_OLECREATE_EX macro declares the class factory of a control that does not require licensing.

IMPLEMENT_OLECREATE_EX

The IMPLEMENT_OLECREATE_EX macro implements the class factory for a control. The macro requires 13 arguments (class_name, external_name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, and b8), which identify the name of the class, the object’s exposed name, and the CLSID for the class.

BEGIN_OLEFACTORY

The BEGIN_OLEFACTORY macro identifies the beginning of the class factory definition. The macro requires the name of the class that is constructed by this factory.

END_OLEFACTORY

The END_OLEFACTORY macro identifies the end of the class factory definition. The macro accepts the same name of the class that appears as an argument to the associated begin macro.

Client/Container Common Commands

Communication between the OLE document server and container, especially with regard to menu command execution, has been cumbersome in previous releases of MFC. The recent addition of Active document support allows the invocation of common menu commands in a standard manner.



ON_OLECMD

The ON_OLECMD macro routes commands through the IOleCommandTarget interface, which enables bidirectional communication between a container application and any DocObjects. Thus, common document-oriented commands, such as Open, Save, and Print, can be exchanged between a container and an object it contains. This macro requires three arguments: a command group identifier (or NULL for standard group designation), the OLE command identifier, and the menu (or other dispatch command) ID. See Table 11.1 for a description of the macros in which the IOleCommandTarget standard menu commands are implemented.

Table 11.1 The Macros That Implement the IOleCommandTarget Menu Commands

Macro Description

ON_OLECMD_CLEARSELECTION() Initiates the Edit Clear command
ON_OLECMD_COPY() Initiates the Edit Copy command
ON_OLECMD_CUT() Initiates the Edit Cut command
ON_OLECMD_NEW() Initiates the File New command
ON_OLECMD_OPEN() Initiates the File Open command
ON_OLECMD_PAGESETUP() Initiates the File Page Setup command
ON_OLECMD_PASTE() Initiates the Edit Paste command
ON_OLECMD_PASTESPECIAL() Initiates the Edit Paste Special command
ON_OLECMD_PRINT() Initiates the File Print command
ON_OLECMD_PRINTPREVIEW() Initiates the File Print Preview command
ON_OLECMD_REDO() Initiates the Edit Redo command
ON_OLECMD_SAVE() Initiates the File Save command
ON_OLECMD_SAVE_AS() Initiates the File Save As command
ON_OLECMD_SAVE_COPY_AS() Initiates the File Save Copy As command
ON_OLECMD_SELECTALL() Initiates the Edit Select All command
ON_OLECMD_UNDO() Initiates the Edit Undo command

Control Property Persistence

A control allows the storing of its properties between activations. This is known as persistence. The mechanism that allows this storage and retrieval to take place is known as serialization. In serializing properties, a stream of property information is written to a storage device. At a later time, the contents of this stream is read back in the order in which it was written, and defines the control’s property settings. Aside from the order, which must remain the same between read and write operations, the size of each property value must also remain the same—or the stream is meaningless.

The MFC framework supports a number of functions and macros to support control persistence. The DoPropExchange member function of the COleControl class offers you a convenient way to support the property persistence model. The function requires one parameter, a CPropExchange object pointer, establishing the property exchange context. DoPropExchange works in conjunction with a number of PX_ prefix functions that aid in the storage and retrieval of common property datatypes.

void CMyCtrl::DoPropExchange(CPropExchange* pPX)
{
   COleControl::DoPropExchange(pPX);
   PX_Bool(pPX, _T(“BoolProp”), m_boolProp, TRUE);
   PX_Short(pPX, _T(“ShortProp”), m_shortProp, 0);
   PX_Color(pPX, _T(“ColorProp”), m_colorProp, RGB(0xFF,0x00,0x00));
   PX_String(pPX, _T(“StringProp”), m_stringProp, _T(“”));
}

An issue that you must address when implementing control persistence is version support. As a control evolves, it will acquire more properties that it must manage (generally speaking). Because the control’s properties are written to a stream, the control must be able to determine the version of the control that wrote the stream and read back only those properties that were present when that version of the control created the stream.

void CMyCtrl::DoPropExchange(CPropExchange* pPX)
{
   ExchangeVersion(pPX, MAKELONG(_wVerMinor, _wVerMajor));
   COleControl::DoPropExchange(pPX);
...
}

Call this function within your control’s DoPropExchange member function to serialize or initialize a property of type BOOL. The property’s value will be read from or written to the variable referenced by bValue, as appropriate. If bDefault is specified, it will be used as the property’s default value. This value is used if, for any reason, the control’s serialization process fails. Table 11.2 lists the functions you can call within DoPropExchange and describes their use.



Table 11.2 Functions Called Within Your Control’s DoPropExchange Member Function

Function Description

PX_Blob The PX_Blob function serializes or initializes a property that stores Binary Large OBject (BLOB) data.
PX_Bool The PX_Bool function serializes or initializes a Boolean property.
PX_Color The PX_Color function serializes or initializes an OLE_COLOR type property. The property window will automatically display a color-picker dialog box that allows you to click the color you want, rather than entering the numeric values for the color. Internally, an OLE_COLOR type is a Long.
PX_Currency The PX_Currency function serializes or initializes a currency type property.
PX_DataPath The PX_DataPath function serializes or initializes.
PX_Double PX_Double function serializes or initializes.
PX_Font The PX_Font function serializes or initializes.
PX_Float The PX_Float function serializes or initializes.
PX_IUnknown The PX_IUnkown function serializes or initializes.
PX_Long The PX_Long function serializes or initializes.
PX_Picture The PX_Picture function serializes or initializes.
PX_Short The PX_Short function serializes or initializes.
PX_ULong The PX_ULong function serializes or initializes.
PX_UShort The PX_UShort function serializes or initializes.
PX_String The PX_String function serializes or initializes.
PX_VBXFontConvert The PX_VBXFontConvert function serializes or initializes.

Dialog Data Exchange

The MFC framework supports a standard mechanism for initializing and validating controls in a dialog box. You are certainly familiar with a number of the Dialog Data Exchange (DDX) functions and dialog data validation (DDV) functions that ClassWizard generates for you when you are laying out dialog boxes. MFC supplies a number of DDX and DDV functions to aid in the exchange and validation of data with OLE controls.

The general case for these functions includes the following parameters:

  A pointer to a CDataExchange object. The framework supplies this object to establish the context of the data exchange, including its direction.
  The ID of an OLE control in the dialog box.
  The control’s dispatch ID.
  A reference to a member variable of the dialog box, with which data is exchanged.

The specific exchange and validation macros appear in Table 11.3.

Table 11.3 The Exchange and Validation Macros

Function Description

DDX_OCBool Manages the transfer of a BOOL datatype property between a control and its container.
DDX_OCBoolRO Manages the transfer of a read-only BOOL datatype property, between a control and its container.
DDS_OCColor Manages the transfer of an OLE_COLOR datatype property between a control and its container.
DDX_OCColorRO Manages the transfer of a read-only OLE_COLOR datatype property between a control and its container.
DDX_OCFloat Manages the transfer of a double datatype property between a control and its container.
DDX_OCFloatRO Manages the transfer of a read-only double datatype property between a control and its container.
DDX_OCInt Manages the transfer of an int datatype property between a control and its container.
DDX_OCIntRO Manages the transfer of a read-only int datatype property between a control and its container.
DDX_OCShort Manages the transfer of a short datatype property between a control and its container.
DDX_OCShortRO Manages the transfer of a read-only short datatype property between a control and its container.
DDX_OCText Manages the transfer of a CString datatype property between a control and its container.
DDC_OCTextRO Manages the transfer of a read-only CString datatype property between a control and its container.
DDX_MonthCalCtrl Manages the transfer of a CTime or COleDateTime datatype property between a control and its container.
DDX_DateTimeCtrl Manages the transfer of a CTime or COleDateTime datatype property between a control and its container.
DDV_MinMaxDateTime Verifies the date or time value in the date-time control.



Dispatch Maps

Dispatch maps offer a way to call automation methods and get or set automation properties. Interfacing with OLE automation in this manner requires the map constructs you are already familiar with, including map declaration, begin and end map designators, and map entry macros.

BEGIN_DISPATCH_MAP

The BEGIN_DISPATCH_MAP macro designates the beginning of a block of one or more dispatch macros that identify an object’s OLE automation methods and properties. This macro requires two arguments: theClass and baseClass. The first argument, theClass, identifies the class in which the map is declared. The second argument, baseClass, (not surprisingly) identifies the base class of theClass. The purpose of supplying both the base class and its superclass is to enable the chaining of maps—a mechanism that is common throughout the MFC framework and not special to the OLE class hierarchy.

END_DISPATCH_MAP

The END_DISPATCH_MAP macro indicates the end of a dispatch map definition. This macro requires no arguments and generates an entry that flags the end of the dispatch map.

DECLARE_DISPATCH_MAP

The DECLARE_DISPATCH_MAP macro declares the dispatch map by defining the variables, structures, and functions that support the dispatch map’s functionality. This macro appears in your class declaration. The macro requires no arguments.

DISP_DEFVALUE

The DISP_DEFVALUE macro defines a special use case of automation properties for Visual Basic. This macro identifies an existing property as the default value for the object. The macro requires two arguments, the name of the automation class, and the external name of a property whose definition occurs elsewhere in the map. The concept behind the default value property is to allow easier programming of the object. Thus the value of the object is modified as the result of an assignment to the object itself.

DISP_FUNCTION

The DISP_FUNCTION macro can appear in a dispatch map, between the BEGIN_DISPATCH_MAP and END_DISPATCH_MAP macros, and identifies an OLE automation method. This macro requires five arguments, describing the name of the method, the return type, and the argument list.

DISP_PROPERTY

The DISP_PROPERTY macro can appear in a dispatch map and identifies an automation property. This macro requires four arguments, describing the name of the property and the property’s datatype. Automation properties differ from automation methods in that they can result in the generation of two interface methods: one to set the property and the other to get the property. Read-only properties only define a get property method.

DISP_PROPERTY_EX

The DISP_PROPERTY_EX macro can appear in a dispatch map and also identifies an automation property. This macro differs from DISP_PROPERTY in that it enables you to define the name of both the set method and the get method exposed by the interface.

DISP_PROPERTY_NOTIFY

The DISP_PROPERTY_NOTIFY macro can appear in a dispatch map and identifies an automation property that automatically calls a function when the value of the property changes. This macro requires five arguments:

  theClass—The name of the class
  szExternalName—The property’s external name
  memberName—The member variable where the property is stored
  pfnAfterSet—The name of the notification function for the external name
  vtPropType—The property’s type

DISP_PROPERTY_PARAM

The DISP_PROPERTY_PARAM macro can appear in a dispatch map and identifies an automation property. This macro accepts, in a sense, a variable number of parameters that can be appended to the end of the required six arguments. The purpose of this property macro variation is to support indexing of the property.

Event Maps

Event maps are an MFC convention that simplify the programming of both control and container applications. ActiveX controls use events to notify their containers that something important has occurred. Examples of such events are a user pressing a key or a clicking a mouse. The word fire is used when describing the act of a control that issues event; hence, the control fires events to the container. The container then responds to the event appropriately.

Map Definition

A control’s event map requires at minimum, the begin and end macros in the definition (.CPP) file of the COleControl-derived class and the declare macro in the class declaration (.H) file. If you begin your project using AppWizard or ControlWizard, these macros will already be in place, in the proper files.

DECLARE_EVENT_MAP

The DECLARE_EVENT_MAP macro resides in a COleControl-derived class declaration and provides a map of the events that the control can fire. The macro requires no arguments.

BEGIN_EVENT_MAP

The BEGIN_EVENT_MAP macro identifies the beginning of the event map. The macro requires two arguments, the name of the control class on which the map is being defined and the name of the base class.

END_EVENT_MAP

The END_EVENT_MAP macro identifies the end of the control’s event map and requires no arguments.

Event Mapping Entries

The event entry mapping macros identify the control functions that fire each event. You can use ClassWizard to add event entries to the control’s event map.

EVENT_CUSTOM

The EVENT_CUSTOM macro can reside in the event map, between the BEGIN_EVENT_MAP and END_EVENT_MAP macros, and identifies a custom event. The macro requires three arguments: the name of the event, the callback function, and a parameter list. The last argument allows a variable number of event parameters, in that multiple parameter datatypes can be supplied, separated by spaces.

EVENT_CUSTOM_ID

The EVENT_CUSTOM_ID macro defines a custom event and specifying a dispatch ID.

Message Mapping Verbs

In grade school you were taught that a verb is an action word. In OLE, verbs are a way for one object, generally a container object, to tell another object, commonly a server object, to perform an action. In fact, although you can define your own verbs, the standard verbs that OLE provides will cover most of your needs. Such standard verbs include open, edit, and print.

ON_OLEVERB

The ON_OLEVERB macro defines a message map entry that associates a custom verb to a specific member function on a control. The macro requires two arguments: the resource string ID containing the verb’s text and the function to call when invoking the verb.

The function to invoke argument must adhere to the following prototype:

BOOL memeberFxn(LPMSG lpMsg, HWND hWndParent, LPCRECT lpRect);

In this prototype, the values for the parameter list are taken from the corresponding IOleObject::DoVerb method.

ON_STDOLEVERB

The purpose of ON_STDOLEVERB is to enable you to override the default behavior for a standard verb. The macro requires two arguments: the standard verb’s index, and the function to call on verb invocation.



Sink Maps

Sink maps are an MFC construct that manages ActiveX control events in a container application. These sink maps offer a mechanism to deal with ActiveX controls that is similar to the way normal Windows controls are managed by message maps. The entries in a sink map associate event handlers with ActiveX controls.

BEGIN_EVENTSINK_MAP

The BEGIN_EVENTSINK_MAP macro identifies the beginning of a container’s event sink map. As with all macros that begin a map definition, this macro requires two arguments: the name of the class on which implementation is occurring, and the name of the base class.

DECLARE_EVENTSINK_MAP

The DECLARE_EVENTSINK_MAP macro resides in the container classes’ declaration and specifies the events for which the container receives notification.

END_EVENTSINK_MAP

The END_EVENTSINK_MAP macro accepts no arguments and indicates the end of the container class’s sink map.

Sink Mapping

ClassWizard generates a number of sink map entry macros that allow a container to respond to events from a control.

ON_EVENT

The ON_EVENT macro can be an entry in a container’s event sink map, and defines an event handler function that responds to an event fired from a control. The macro requires five arguments, among which are a pointer to the container function that responds to the event and the argument datatypes for the method.

ON_EVENT_RANGE

The ON_EVENT_RANGE macro identifies an event handler for an OLE control event that maps to a contiguous range of ids. This macro is a map entry that can appear between the BEGIN_EVENTSINK_MAP macro and the END_EVENTSINK_MAP macro.

ON_EVENT_REFLECT

The ON_EVENT_REFLECT macro offers message reflection that is analogous to the message reflection that MFC supports for a number of common controls. In this case, the macro allows the control to receive events before they are handed off to the control’s container. This macro requires four arguments, and it must appear as an entry between the begin and end macros of the sink map.

ON_PROPNOTIFY

The ON_PROPNOTIFY macro defines a sink map entry that handles property notifications of an OLE control.

ON_PROPNOTIFY_RANGE

The ON_PROPNOTIFY_RANGE macro is another event sink map entry that enables you to define a single handler function for a range of OLE control IDs.

ON_PROPNOTIFY_REFLECT

The ON_PROPNOTIFY_REFLECT macro enables you to assign a function that receives an OLE control’s property notification prior to the container receiving the event. The handler function returns a Boolean state of true or false, thus either allowing the property to change or disallowing such an action.

Connection Maps

The connection map macros work in conjunction with the CConnectionPoint class. Connection maps are MFC’s implementation of OLE automation’s outgoing interfaces. This mechanism allows server objects to make calls on a client object.

BEGIN_CONNECTION_PART

The BEGIN_CONNECTION_PART macro identifies the beginning of a list of CONNECTION_IID entries, where each entry identifies the sink’s interface identifier (IID).

END_CONNECTION_PART

The END_CONNECTION_PART macro designates the end of the CONNECTION_IID entries.

CONNECTION_IID

The CONNECTION_IID macro is a connection map entry and always appears between the BEGIN_CONNECTION_PART and END_CONNECTION_PART macros. The macro requires a single argument, which identifies the IID of the sink interface.

DECLARE_CONNECTION_MAP

The DECLARE_CONNECTION_MAP macro associates a connection map with a COleControl-derived class. This macro accepts no arguments and resides in the class declaration.

BEGIN_CONNECTION_MAP

The BEGIN_CONNECTION_MAP macro identifies the beginning of the connection point map for your control. This macro requires a single argument, which is the name of the class that contains the connection points.

END_CONNECTION_MAP

The END_CONNECTION_MAP macro identifies the end of the connection point map for the control. The macro accepts no arguments.

CONNECTION_PART

The CONNECTION_PART macro is a connection map entry that can reside between the BEGIN_CONNECTION_MAP macro and the END_CONNECTION_MAP macro.

Property Page Data Mapping

The DDP_ functions synchronize property page dialog member variables with an ActiveX control’s properties. The DDP_ functions are similar to their DDX_ counterparts, with the addition of a single string argument at the end of the function that accepts the name of the control property. The argument list for a DDP_ function follows:

  Pointer to a CDataExchange object. The framework supplies this object to establish the context of the data exchange, including its direction.
  The resource ID of the combo box control associated with the control property specified by pszPropName.
  The member variable associated with the property page control specified by ID and the property specified by pszPropName.
  The property name of the control property to be exchanged with the combo box control specified by ID.

MFC offers a number of functions to exchange data with the most common dialog controls:

DDP_CBIndex Use the DDP_CBIndex function to map a combo box’s selected string’s index with a control property.
DDP_CBString Use the DDP_CBString function to map a partial string match with the string that is selected in a combo box with a control property.
DDP_CBStringExact Use the DDP_CBStringExact function to map an exact string match.
DDP_Check Use the DDP_Check function to map the property’s value with the property page check box control.
DDP_LBIndex Use the DDP_LBIndex function to map an integer property’s value with the index of the current selection in a list box on the property page.
DDP_LBString Use the DDP_LBString function to map a string property’s value with the current selection in a list box on the property page.
DDP_LBStringExact Use the DDP_LBStringExact function to map a string property’s value when it is an exact match of the current selection in a list box on the property page.
DDP_PostProcessing Use the DDP_PostProcessing function to complete the transfer of property values from the property page to your control when property values are being preserved.
DDP_Radio Use the DDP_Radio function to map a property’s value with the associated property page radio button control.
DDP_Text Use the DDP_Text function to map a property’s value with the associated property page control.



Property Pages

Property pages offer a standard mechanism for displaying and allowing user modification of an object’s properties. A tabbed dialog box offers your object’s users a familiar Windows standard for interacting with (that is, getting and setting) property values.

PROPPAGEID

The PROPPAGEID macro adds a property page to an ActiveX control. This macro requires a single argument, which is the CLSID of the property page. This macro is an entry in the property page map and must be declared between the BEGIN_PROPAGEIDS and END_PROPPAGEIDS macros in the control’s class implementation file.

DECLARE_PROPPAGEIDS

The DECLARE_PROPPAGEIDS macro resides in your control’s class declaration file and identifies the property pages that belong to the control.

BEGIN_PROPPAGEIDS

The BEGIN_PROPPAGEIDS macro identifies the beginning of an ActiveX control’s property page map. The macro requires two arguments, the name of the control’s class and the number of property pages the class uses.

END_PROPPAGEIDS

The END_PROPPAGEIDS macro identifies the end of your control’s property page map.

Type Library Access

Type libraries describe the interfaces implemented by an object server. Such a description includes the number of parameters, the datatype for each parameter, the direction each parameter is passed, and the return value. In addition, type library information includes the names of the methods and properties, as well as the location of help files describing their use.

DECLARE_OLETYPELIB

The DECLARE_OLETYPELIB macro defines the GetTypeLib member function for a control’s class. This macro accepts a single argument—the name of the control’s class. This macro must appear in the class declaration.

IMPLEMENT_OLETYPELIB

The IMPLEMENT_OLETYPELIB macro implements the control’s GetTypeLib member function. The macro requires four parameters, including the major and minor version numbers of the control’s type library.

MFC and the OLE Class Categories

MFC’s COM support focuses on the technologies that rely heavily on visual integration with other components and applications.

Active Document

An active document is what old-timers will remember as OLE document servers. Essentially an active document is a complex COM server object that, when activated in a container application, takes over the entire client workspace and hijacks all the user interface components, including the toolbars, menus, and so on. Figure 11.1 shows an active document server in an active state in a container. In this case, the server is Microsoft Word and the container is Internet Explorer.


Figure 11.1  An active document in acontainer.

CDocObjectServer

The CDocObjectServer class inherits directly from the CCmdTarget class, and you use it when building applications that implement OLE server documents. This class implements the following interfaces: IOleCommandTarget, IOleDocument, IOleDocumentView, and IPrint. This class is necessary in making a normal COleDocument server, which is only a simple OLE container, into a full DocObject server.

A DocObject server, also referred to as an active document, allows in-place activation in the container application. Common container applications include Microsoft Office Binder and Microsoft Internet Explorer.

CDocObjectServerItem

The CDocObjectServerItem class inherits directly from the COleServerItem class and is necessary in the construction of DocObject servers. The CDocObjectServerItem constructor requires a pointer to a COleServerDoc object, which will contain the new DocObject item that CDocObjectServerItem represents. The class also provides three overridable functions: OnHide, OnOpen, and OnShow. These functions enable you to perform custom operations at various times of the DocObject’s lifecycle.

COleDocObjectItem

The COleDocObjectItem class inherits from the COleClientItem class. A COleDocObjectItem is managed by an OLE container application, and it implements the interfaces necessary for active document containment. An active document differs from a normal container object in that it occupies the entire client area during in-place activation and it assumes full control of the container application’s Help menu.

Automation

The origin of OLE lies in some pretty obscure documentation that speaks of variant datatypes, type library information spelunking, and the ever-cryptic MkTypLib utility. Well, automation has come a long way since those times and is almost the undisputed king of the COM-enabling technologies. Automation provides the lingua franca for applications, ActiveX controls, COM object servers, and scripting languages to speak to one another. Although automation’s IDispatch interface isn’t the fastest way for COM-aware objects to communicate, the capability of a program to discover the methods, properties, and associated argument datatypes and return value at runtime is a very powerful feature.

CCmdTarget

The CCmdTarget class is one of the MFC framework’s most familiar classes. It seems as if you can’t do anything without deriving from, or making calls to, this most common base class. Although not strictly an OLE class, because CCmdTarget forms the heart of MFC’s message routing architecture by supporting message maps, it also enables automation by exposing IDispatch functionality with dispatch maps. You can create an automation server by deriving a class from CCmdTarget and using ClassWizard to create the necessary dispatch map entries and ODL text.

CConnectionPoint

The CConnectionPoint class derives from CCmdTarget and implements the IConnectionPoint interface. Connection points are commonly referred to as outgoing interfaces. The most common use of outgoing interfaces is ActiveX control events. In this case, the ActiveX control container responds to events, whose source is the control. Connection point management requires support of the CONNECTION_MAP macros.


Note:  

The COleControl class implements two connection points by default: one for property change notification and the other for property change events.


COleDispatchDriver

The COleDispatchDriver class is the heart of client-side automation for MFC applications. This class provides the necessary member functions for invoking automation server methods as well as obtaining and manipulating automation properties. The most common deployment of this object involves the use of ClassWizard where you add a class to your project whose source is a type library. ClassWizard then constructs the appropriate wrapper class, deriving it from COledispatchDriver. The properties and methods of the automation server are then exposed to your program as functions on this class.

COleDispatchException

The COleDispatchException class derives from CException and is thrown as the result of trying to complete an OLE automation operation. You can issue an exception of this type by calling AfxThrowOleDispatchException.

Common Dialogs for OLE

Microsoft provides a familiar set of common dialogs for the Windows operating system, including File Open, Print, File Save, and File Save As. There probably isn’t a Windows program on the planet that doesn’t present one or more of this dialog boxes. Likewise, Microsoft offers a number of common dialog boxes for those operations that are OLE-centric. You will notice, however, that with the exception of the COleBusyDialog and COlePropertiesDialog, these dialogs offer support for active document containment and management.



COleBusyDialog

The COleBusyDialog class offers a standard user interface component to the user that indicates whether an OLE document or automation server is either not responding or unavailable. This class offers OLEUIBUSY.

This class requires the support of the OLEUIBUSY structure for purposes of initialization and format of the dialog. In addition, this structure offers a field to identify the user’s response to the display of this dialog. Finally, this structure offers access to hook the message loop that is active during the modal display of this dialog for purposes of intercepting messages that are intended for the dialog box.

COleChangeIconDialog

The COleChangeIconDialog class displays a dialog box, which enables the user to identify the icon that is displayed for an embedded or linked OLE-document item. Although this class offers you (a programmer) the ability to display this dialog under program control, it is commonly displayed as the result of user actions taken from many of the other standard OLE-document management dialog boxes, which include Insert Object, Paste Special, and Convert.

The use of this class requires the OLEUICHANGEICON structure, which contains fields to manage initialization of the Change Icon dialog box and offers a field that accepts the return information when user interaction with the dialog completes. This structure offers fields to support a hook to the message loop that is active during the modal display of this dialog for purposes of intercepting messages that are intended for the dialog box.

COleChangeSourceDialog

The COleChangeSourceDialog class displays an OLE standard dialog box that enables a user to modify an OLE document item’s link. Construction of an object of this class requires a pointer to the COleClientItem-derived class object whose source attributes are being modified. This class provides a number of member functions to obtain the name of the object and its moniker. Use of this class requires an instance of the OLEUICHANGESOURCE structure.

ColeConvertDialog

The COleConvertDialog class derives from COleDialog and enables you to change the document server object that is associated with an embedded or linked item. As with most of the OLE common dialogs, this dialog also requires a structure to define properties on initialization and provide a place for return information. In this case, the COleConvertDialog class requires a OLEUICONVERT structure.

COleDialog

The COleDialog class is the base class for all standard OLE dialogs. This class derives from CCommonDialog and offers only a single member function—GetLastError. GetLastError returns an error code that is specific to deriving dialog class.

COleInsertDialog

The COleInsertDialog class aids in the creation of one of the most common standard OLE dialogs—the Insert Object dialog. This dialog is certainly familiar to anyone who is a Windows user and is present in any OLE-compatible document container. COleInsertDialog offers a member function, CreateItem, which creates an object of the type identified by the dialog on exit. The OLEUIINSERTOBJECT structure supports this class.

COleLinksDialog

The COleLinksDialog class can display the OLE Edit Links dialog box. The OLEUIEDITLINKS structure supports this class.

COlePasteSpecialDialog

The COlePasteSpecialDialog class enables you to display an OLE common dialog that aids a user in the process of linking or embedding data in your application’s compound document. The user can also select to render the data in its native format or simply represent the data with an icon.

COlePropertiesDialog

The COlePropertiesDialog class offers a user interface component, which is common to the Windows operating system and supports modification of an OLE object’s properties. This is the same properties dialog that is displayed when a user right-clicks any desktop icon and selects the Properties menu. Figure 11.2 shows the COM and MFC Properties dialog.

The OLE Object Properties dialog contains three property pages by default: General, View, and Link. Unlike the other OLE common dialogs, this dialog makes use of the following five structures:

  OLEUIGNRLPROPS—Supports the General page of the dialog box.
  OLEUILINKPROPS—Supports the Link page of the dialog box.
  OLEUIOBJECTPROPS—Initializes the Object Properties dialog box, containing pointers to all the page structures.
  OLEUIVIEWPROPS—Supports the View page of the dialog box
  PROPSHEETHEADER—Supports custom property sheets


Figure 11.2  The COM and MFC Properties dialog.

COleUpdateDialog

The COleUpdateDialog class provides an OLE standard dialog allowing a user to update existing linked or embedded objects in an OLE document. This class derives directly from the COleLinksDialog and offers no additional functions or data members—the user interface is simply tailored to this specific case of the COleLinksDialog.

Container

The MFC framework provides the document/view architecture as a model for separating the management of a program’s data from the visual presentation of such data. The framework offers the base class CView, from which specific user interface views are derived, and the CDocument class, which offers a base for data management. This model has been extended to offer support for OLE documents with the COleDocument class. OLE documents must manage data whose format is defined by external applications. These data sources can either be embedded or linked to the document. Embedded data is stored inside the file along with the data that is native to your application, whereas linked data is stored outside your application’s data file and referenced by a moniker. A moniker uniquely identifies a COM object. Until recently this moniker was almost certainly a file-moniker, thus providing path information to the data file. However, today it would not be uncommon for this link to exist as a url-moniker. Figure 11.3 contains a decision tree that might help you identify the OLE document class that you require.


Figure 11.3  OLE ServerDoc decision tree.

CDocItem

The CDocItem class derives from CCmdTarget and is the base class for COleClientItem, COleServerItem, and CDocObjectServer. The class offers a single method, GetDocument, and one overridable method, IsBlank.

COleClientItem

The COleClientItem class can be used in conjunction with the COleDocument, ColeLinkingDoc, or COleServerDoc classes (see Figure 11.4). Each OLE item that is managed by the document is wrapped in this class.


Figure 11.4  How documents manage items.

COleDocument

The CDocument base class offers methods that manage the views to the data. Storing and retrieving data, as well as managing the dirty or change state information for the document, falls under the list of responsibilities for this class.



COleLinkingDoc

The COleLinkingDoc class derives from the COleDocument class and offers the additional functionality of linking support to your application’s data management model.

CRichEditCntrItem

A common OLE-enabled document type is an edit control. Microsoft provides access to the edit control that is used by the WordPad utility with the CRichEditCtrl class. In addition, the CRichEditCntrItem class provides container-side access to the COleClientItems that are stored in the control.

The CRichEditCntrItem class works in conjunction with the CRichEditView and CRichEditDoc classes in providing rich edit container support for OLE server objects that jives with MFC’s document view architecture. The CRichEditView performs those functions in displaying text and embedded object placeholders, whereas CRichEditDoc maintains a list of COleClientItems. CRichEditCntrlItem derives directly from COleClientItem and offers a single additional member function, which creates a CRichEditCntrlItem object and adds it to the container document.

CRichEditDoc

The CRichEditDoc class derives from COleServerDoc and manages OLE client items for a CRichEditView.

Control

What do controls, OLE Controls, ActiveX controls, and even OCXs have in common? Everything. Leave marketing people to their own devices long enough and before long, they’ll get bored with the name—and change it. The fact of the matter is though, that the widespread popularity of the Internet forced Microsoft’s hand to redefine an OLE control. Initially, these components were designed to replace the VBX architecture, but soon they were needed to shore up Microsoft’s defense against Java and the JavaBean component model.

CAsyncMonikerFile

The CAsyncMonikerFile class derives from CMonikerFile and offers asynchronous monikers to ActiveX controls. The purpose of asynchronous monikers is to allow ActiveX controls to appear more responsive during activation. This class offers a number of member functions that allow control over a callback and transfer progress.

CCachedDataPathProperty

The CCachedDataPathProperty inherits from CDataPathProperty and is a variation on the base class’s theme. The CCachedDataPathProperty also allows the asynchronous loading of an OLE control property; however, this class stores the contents in RAM.

CDataPathProperty

The CDataPathProperty inherits from CAsyncMonikerFile and allows the asynchronous loading of an OLE control property. This class enables you to download large control properties, such as image files, in the background without blocking initialization of the control.

CFontHolder

The CFontHolder class wraps the IFont interface. This class helps you manage ActiveX control font properties.

CMonikerFile

The CMonikerFile class derives from COleStreamFile. COleStreamFile wraps the IStream interface, whereas CMonikerFile identifies the stream with a moniker.

COleCmdUI

The COleCmdUI class offers a mechanism for a DocObject and a container application to exchange commands. Thus, a container can receive and process commands that originate from a DocObject’s user interface and vice versa.

COleControl

The COleControl class is the class you base your ActiveX controls on when developing such controls with MFC. This class provides you with a huge toolkit of functions, attributes, and overridable methods. This class alone requires an entire chapter to describe (and in fact this book includes such a discussion in Chapter 14, “MFC ActiveX Controls”) the flexibility that the COleControl provides you in constructing ActiveX controls. Although MFC is very capable in developing such controls, you must remember that for these controls to work, the target machine must have the correct MFC DLLs installed. Also, the controls that you develop with this framework can result in some sizeable code, which might be an issue for users downloading such controls from a slow Internet connection. Microsoft of course comes to the rescue, or saw this deficiency, and provides another technology for building lightweight COM objects, including ActiveX controls, with ATL.

COleControlModule

The COleControlModule class derives from CWinApp and is to MFC-based ActiveX controls what the CWinApp class is to MFC applications. The COleControlModule provides only two virtual functions, InitInstance and ExitInstance, and as with CWinApp, these are the places that you put your initialization and termination code, respectively. The InitInstance function calls AfxOleInitModule for you and also calls COleObjectFactory’s RegisterAll function, thus initializing the OLE DLLs and registering your control’s class factories. The ExitInstance function revokes class factory registration.

COlePropertyPage

The COlePropertyPage class derives from CDialog and displays an ActiveX control’s property pages. If you use ControlWizard to build your control, it will automatically generate a single property page that derives from this class. If you want to add additional property pages to your control, you must create your own classes that also derive from COlePropertyPage. Getting your control to recognize these additional pages requires editing the PROPPAGEID map. You must add the additional PROPPAGEID entry macros, supplying the GUID that you generate for each page.

CPictureHolder

The CPictureHolder class helps you manage picture properties in your ActiveX control. This class helps you display images in your control that originate as bitmap, icon, or metafile sources. You can also get and set the IPictureDisp interface with a pair of member functions. You can obtain various picture attributes through the IPictureDisp interface, such as the height and width of the image.

CPropExchange

The CPropExchange class aids in the serialization of an ActiveX control’s properties. Your control’s DoPropExchange function receives a pointer to a CPropExchange object where it provides context for all the PX_ methods that you call to serialize or initialize a property.

Drag and Drop (Universal Data Transfer)

The OLE drag-and-drop functions involve a data transfer protocol that involves exchanging an IDataObject pointer between a source and a destination.

COleDataObject

The COleDataObject class is central to MFC’s uniform data transfer (UDT) implementation. This class enables you to easily access the IDataObject interface, which enumerates data formats, enables data transfer, and advises of data change. COleDataObjects are created on the receiving side of a data transfer—for example, the paste operation of a clipboard transfer or the drop operation of a drag-and-drop transfer.

COleDataSource

The COleDataSource class derives from CCmdTarget and offers a source for data transfer. Unlike ColeDataObjects, which come into play at the receiving end of a UDT operation, COleDataSource objects are created at the initiating side of such a transfer. The act of copying to a clipboard or selecting data for a drag-and-drop operation results in the creation of a COleDataSource object.



COleDropSource

The COleDropSource class offers a convenient mechanism for initiating uniform data transfer in an interactive manner, observing standard OLE user interface conventions. The class offers three overridable functions that are called during the appropriate times of the drop-target selection process.

COleDropTarget

The OLE classes that support drag-and-drop operations rely heavily upon the interaction between the OLE uniform data transfer mechanism and various user interface elements that are identified as data sources and drop targets. In addition, a number of overridable methods allow customization of user feedback when the mouse cursor, engaged in a drag-and-drop operation, hovers over a window registered as a drop target.

Document Servers

The classes that fall within the Document Server heading include those classes that manage the workspace or client area of the container application.

CDocItem

The CDocItem is useful to both container applications and document server applications. If you are building an OLE container or control, you will not instantiate the CDocItem class directly—you will use one of its derived classes instead. Figure 11.5 shows the CDocItem base class.


Figure 11.5  The CDocItem, base class.

There might be times, however, when you are building an OLE-aware container and have reason to manage non-OLE document items. The COleDocument class treats a document as a collection of CDocItem objects (see Figure 11.6).


Figure 11.6  Components of a document.

When you select compound document support for a container and check Active Document Container, you get code that displays the Insert Object dialog as part of the framework that AppWizard builds for you. You might also want to support document components that are not OLE document servers. In this case, you derive your own class from CDocItem and implement the necessary functions.

COleIPFrameWnd

The COleIPFrameWnd class provides the necessary functionality for an application’s window that supports in-place activation of OLE document server objects. This class aids in the management of client area screen real estate and control bar positioning.

COleResizeBar

The COleResizeBar class derives from the MFC CControlBar class and supports the resizing of in-place OLE items.

COleServerDoc

The COleServerDoc class is the base class for OLE document servers.

COleServerItem

The COleServerItem class, derived from CDocItem, manages interaction between a server document and its container application.

COleTemplateServer

The COleTemplateServer class offers support to applications that implement OLE servers—both automation servers and document servers. The COleTemplateServer class derives from the COleObjectFactory class and adds two additional methods.

Support

The classes that this section describes do not fall under any particular category. They are, however, vital to the implementation of one or more of the OLE class categories.

COleMessageFilter

The COleMessageFilter class is useful in managing the concurrency that is necessary for OLE automation and document server tasks. This class is derived from CCmdTarget, and an instance of this object is automatically created for the MFC framework as a result of a call to initialize the OLE libraries with AfxOleInit(). Among the methods this class supports are those to set and reset an application’s busy state, and those that register and revoke the message filter with the OLE system libraries. An MFC-based application’s message filter object can be retrieved with a call to AfxOleGetMessageFilter.

COleObjectFactory

The COleObjectFactory class offers flexible support for OLE object creation, registration, and licensing. A class factory object creates objects of a type that you specify. This class wraps much of the functionality that the IClassFactory and IClassFactory2 interfaces offer.

COleStreamFile

The COleStreamFile class wraps the IStream interface. A stream is the structured storage equivalent of a file in a traditional file system. This class is derived from the standard MFC CFile class, thus aiding you in the migration from standard file I/O to compound file support.

CRectTracker

The CRectTracker class does not have a base class and is useful not only in enabling OLE applications: The CRectTracker class draws a rectangular item outline on a screen device context and allows you to interact with it by pulling on its handles. Thus you can move and resize the rectangle in any direction. The CRectTracker object is commonly found in OLE container applications, allowing a user to arrange server objects.

COleException

The COleException class is derived from MFC’s CException class and contains a status code that identifies the condition that caused the exception. Objects of this class are generated as a result to calls to AfxThrowOleException.

COleCurrency

The COleCurreny class wraps the currency datatype. This class overloads the constructor and assignment operator, providing you much flexibility in exchanging currency values with any automation-compatible VARIANT, whose value is of the VT_CY type. Additionally, this class provides methods to access the units and fractional units that constitute the currency value.

COleDateTime

The COleDateTime class wraps the DATE datatype. As with the COleCurrency class, COleDateTime also overloads the constructor and assignment operator, extending the class’s capability to exchange date values with any automation compatible VARIANT, whose value is of the VT_DATE type. In addition, this class simplifies date time information with other common formats, including SYSTEMTIME and FILETIME structures. Other member functions enable you to get and set individual components of the time and date, as well as parse date/time strings.

COleDateTimeSpan

The COleDateTimeSpan class is useful in determining and manipulating a time span value. Various constructors are available, and you can specify a value of time in several ways, including the day, hour, and minute values or as a single floating-point value (a value of 1.5, for example, yields 1 day and 12 hours).

COleDBRecordView

The COleDBRecordView class inherits from CFormView and is useful for displaying the fields that constitute a row in a database table. This class works in conjunction with a CRowSet object, using DDX function to populate dialog controls with record data.

Summary

This chapter offers a quick overview of the classes, structures, and functions that compose the OLE support of MFC. This support is broad and deep, allowing you to build applications that exploit almost every COM category. The MFC classes place an emphasis on the visual COM technologies, such as those that require in-place activation. Server objects that require this support must provide complex activation code in order to achieve proper integration with their containers. The Microsoft Foundation Classes are a perfect way to achieve this level of compatibility, leveraging a large body of reliable code.